{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Large HW Swings\n",
    "\n",
    "Supported setups:\n",
    "\n",
    "SCOPES:\n",
    "\n",
    "* OPENADC\n",
    "* CWNANO\n",
    "\n",
    "PLATFORMS:\n",
    "\n",
    "* CWLITEARM\n",
    "* CWLITEXMEGA\n",
    "* CWNANO"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SCOPETYPE = 'OPENADC'\n",
    "PLATFORM = 'CWLITEARM'\n",
    "CRYPTO_TARGET = 'TINYAES128C'\n",
    "num_traces = 50"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash -s \"$PLATFORM\" \"$CRYPTO_TARGET\"\n",
    "cd ../hardware/victims/firmware/simpleserial-aes\n",
    "make PLATFORM=$1 CRYPTO_TARGET=$2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data Tracing Theory"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the last tutorial, we saw how the power measurement of a device is related to the Hamming weight. Let's use this to see where some arbitrary data is processed by a device. We'll later expand on this to perform a test that also takes into account noise.\n",
    "\n",
    "Our objective is simple - we'll send in some data with all 1's in one location, and then some data with all 0's."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Capturing Power Traces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Capturing power traces will be very similar to previous tutorials, except this time we'll be using a loop to capture multiple traces, as well as numpy to store them."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll use some helper scripts to make setup and programming easier. If you're using an XMEGA or STM (CWLITEARM) target, binaries with the correct should be setup for you:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run \"Helper_Scripts/Setup_Generic.ipynb\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fw_path = '../hardware/victims/firmware/simpleserial-aes/simpleserial-aes-{}.hex'.format(PLATFORM)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# program the target\n",
    "cw.program_target(scope, prog, fw_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Capturing Traces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Below you can see the capture loop. The main body of the loop loads some new plaintext, arms the scope, sends the key and plaintext, then finally records and appends our new trace to the `traces[]` list. At the end, we convert the trace data to numpy arrays, since that's what we'll be using for analysis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Capture Traces\n",
    "from tqdm import tnrange\n",
    "import numpy as np\n",
    "import time\n",
    "\n",
    "ktp = cw.ktp.Basic()\n",
    "\n",
    "traces = []\n",
    "\n",
    "for i in tnrange(num_traces, desc='Capturing traces'):\n",
    "    key, text = ktp.next()\n",
    "    \n",
    "    #Currently ALL bits are random. Let's extend bit 0 to a full byte to give us a random 0xFF or 0x00\n",
    "    if text[0] & 0x01:\n",
    "        text[0] = 0xFF\n",
    "    else:\n",
    "        text[0] = 0x00\n",
    "    \n",
    "    trace = cw.capture_trace(scope, target, text, key)\n",
    "    if trace is None:\n",
    "        continue\n",
    "    traces.append(trace)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have our traces, we can also plot them using Bokeh:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure, show\n",
    "from bokeh.io import output_notebook\n",
    "\n",
    "output_notebook()\n",
    "p = figure()\n",
    "\n",
    "xrange = range(len(traces[0].wave))\n",
    "p.line(xrange, traces[2].wave, line_color=\"red\")\n",
    "show(p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# cleanup the connection to the target and scope\n",
    "scope.dis()\n",
    "target.dis()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Trace Analysis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comparing 0xFF to 0x00"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have some traces, let's look at what we've actually recorded. We'll be doing the following tasks:\n",
    "\n",
    "1. Seperate traces into two groups: 0x00, and 0xFF\n",
    "1. Make an average of each group.\n",
    "1. Subtract the two averages and see the difference.\n",
    "\n",
    "This will be shown in the following two cells. Note the number of 0xFF and 0x00 isn't exactly 50/50. That is why we need to ensure we average them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure, show\n",
    "from bokeh.io import output_notebook\n",
    "import numpy as np\n",
    "\n",
    "output_notebook()\n",
    "p = figure()\n",
    "\n",
    "one_list = []\n",
    "zero_list = []\n",
    "\n",
    "for trace in traces:\n",
    "    if trace.textin[0] == 0x00:\n",
    "        one_list.append(trace.wave)\n",
    "    else:\n",
    "        zero_list.append(trace.wave)\n",
    "\n",
    "print(\"Number of 0xFF: \" + str(len(one_list)))\n",
    "print(\"Number of 0x00: \" + str(len(zero_list)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "one_avg = np.asarray(one_list).mean(axis=0)\n",
    "zero_avg = np.asarray(zero_list).mean(axis=0)\n",
    "\n",
    "diff = one_avg - zero_avg\n",
    "\n",
    "p.line(range(0, len(traces[0].wave)), diff)\n",
    "\n",
    "show(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice the large spike as the data is handled. You could also try some of the following if you have time:\n",
    "\n",
    "* Force the OUTPUT data to all 0xFFs or all 0x00s\n",
    "* Force some intermediate value of 0xFFs or all 0x00s\n",
    "* Set multiple bytes to 0xFF vs 0x00\n",
    "* Plot each byte in sequence to see the data movement"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tests"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert (max(abs(diff)) > 0.01), \"Low max difference of {} between 0x00 and 0xFF\".format(max(abs(diff)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
